﻿namespace HuwmanCode.ServiceProcess

open System
open System.ComponentModel
open System.Configuration.Install
open System.Diagnostics
open System.IO
open System.Reflection
open System.ServiceProcess
open System.Text.RegularExpressions
open System.Threading
open HuwmanCode
open HuwmanCode.Diagnostics

///<summary>
/// Indicates the manner in which the service was started
///</summary>
type public Startup =
| Fail = 0           //Failed to start
| Debug = 1          //Started by Visual Studio with debugger attached
| ServiceControl = 2 //Started by the service controller

and internal Base() =
    static member Services = //Finds all services in all loaded assemblies which extend WindowsServiceBase
        AppDomain.CurrentDomain.GetAssemblies()
        |> Array.map(fun a -> a.GetExportedTypes()) |> Array.concat
        |> Array.filter(fun t -> t.IsSubclassOf(typeof<WindowsServiceBase>))
        |> Array.map(fun t -> Activator.CreateInstance(t) :?> WindowsServiceBase)

    static member GetServiceInstallers() = //Creates a ServiceInstaller for each WindowsServiceBase in Services
        let getDescription(service:WindowsServiceBase) =
            let description = service.GetType().GetCustomAttributes(typeof<DescriptionAttribute>, false)
                                |> Array.map(fun o -> o :?> DescriptionAttribute)
                                |> Array.map(fun a -> a.Description)
                                |> Array.tryFind(fun _ -> true) //return the first item
            match description with
            | Some d -> d
            | None -> ""

        Base.Services
        |> Array.map(fun s -> new ServiceInstaller(ServiceName = s.ServiceName,
                                                   Description = getDescription s,
                                                   StartType = ServiceStartMode.Automatic,
                                                   ServicesDependedOn = DependsOnAttribute.GetDependencies s))
        |> Array.map(fun si -> si :> Installer)
        |> Array.toList

    static member Run() = 
        let (|Name|) (proc:Process option) = proc.map(fun p -> (p.ProcessName, p))        
        let run() = Base.Services |> Seq.cast<ServiceBase> |> Seq.toArray |> ServiceBase.Run
        
        match Process.Current.Parent with
        | Name ("services", p) ->
            run()
            Startup.ServiceControl
        | Name ("devenv", p) when Debugger.IsAttached -> 
            Startup.Debug
        | _ -> Startup.Fail

and public WindowsServiceBase(serviceName:string) as this =
    inherit ServiceBase()
    do 
        match serviceName with
        | Text text -> base.ServiceName <- text
        | _ -> base.ServiceName <- this.GetType().FullName

    new () = new WindowsServiceBase(null)

    member internal x.StartAfterInstall
        with get() = StartAfterInstallAttribute.IsDecorated(x)
            
    static member Run() =
        Base.Run()